home *** CD-ROM | disk | FTP | other *** search
/ MacWorld 2003 August / MW 8 2003 CD1.iso / Inside Macworld / Product News / gimp-1.2.4.sit / gimp-1.2.4 / plug-ins / script-fu / script-fu-server.c < prev    next >
Encoding:
C/C++ Source or Header  |  2003-01-15  |  13.4 KB  |  613 lines

  1. /* The GIMP -- an image manipulation program
  2.  * Copyright (C) 1995 Spencer Kimball and Peter Mattis
  3.  *
  4.  * This program is free software; you can redistribute it and/or modify
  5.  * it under the terms of the GNU General Public License as published by
  6.  * the Free Software Foundation; either version 2 of the License, or
  7.  * (at your option) any later version.
  8.  *
  9.  * This program is distributed in the hope that it will be useful,
  10.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12.  * GNU General Public License for more details.
  13.  *
  14.  * You should have received a copy of the GNU General Public License
  15.  * along with this program; if not, write to the Free Software
  16.  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  17.  */
  18.  
  19. #include <glib.h>        /* For G_OS_WIN32 */
  20.  
  21. #ifndef G_OS_WIN32
  22.  
  23. #include <stdarg.h>
  24. #include <stdlib.h>
  25. #include <stdio.h>
  26. #include <string.h>
  27. #include <ctype.h>
  28. #include <unistd.h>
  29. #include <time.h>
  30. #include <errno.h>
  31. #include <sys/time.h>
  32. #include <sys/types.h>
  33. #include <sys/socket.h>
  34. #include <netinet/in.h>
  35. #include <arpa/inet.h>
  36. #include <netdb.h>
  37.  
  38. #include "config.h"
  39.  
  40. #ifdef HAVE_SYS_SELECT_H
  41. #include <sys/select.h>
  42. #endif /* HAVE_SYS_SELECT_H */
  43.  
  44. #include "libgimp/gimp.h"
  45. #include "libgimp/gimpui.h"
  46.  
  47. #include "script-fu-intl.h"
  48.  
  49. #include "gtk/gtk.h"
  50. #include "siod.h"
  51. #include "script-fu-server.h"
  52.  
  53. #define COMMAND_HEADER  3
  54. #define RESPONSE_HEADER 4
  55. #define MAGIC   'G'
  56.  
  57. #ifdef NO_DIFFTIME
  58. #define difftime(a,b) (((double)(a)) - ((double)(b)))
  59. #endif
  60.  
  61. #ifndef NO_FD_SET
  62. #  define SELECT_MASK fd_set
  63. #else
  64. #  ifndef _AIX
  65.      typedef long fd_mask;
  66. #  endif
  67. #  if defined(_IBMR2)
  68. #    define SELECT_MASK void
  69. #  else
  70. #    define SELECT_MASK int
  71. #  endif
  72. #endif
  73.  
  74.  
  75. /*  image information  */
  76.  
  77. /*  Header format for incoming commands...
  78.  *    bytes: 1          2          3
  79.  *           MAGIC      CMD_LEN_H  CMD_LEN_L
  80.  */
  81.  
  82. /*  Header format for outgoing responses...
  83.  *    bytes: 1          2          3          4
  84.  *           MAGIC      ERROR?     RSP_LEN_H  RSP_LEN_L
  85.  */
  86.  
  87. #define MAGIC_BYTE      0
  88.  
  89. #define CMD_LEN_H_BYTE  1
  90. #define CMD_LEN_L_BYTE  2
  91.  
  92. #define ERROR           1
  93. #define RSP_LEN_H_BYTE  2
  94. #define RSP_LEN_L_BYTE  3
  95.  
  96. /*
  97.  *  Local Structures
  98.  */
  99.  
  100. typedef struct
  101. {
  102.   gchar *command;
  103.   gint   filedes;
  104.   gint   request_no;
  105. } SFCommand;
  106.  
  107. typedef struct
  108. {
  109.   GtkWidget *port_entry;
  110.   GtkWidget *log_entry;
  111.  
  112.   gint       port;
  113.   gchar     *logfile;
  114.  
  115.   gint       run;
  116. } ServerInterface;
  117.  
  118. /*
  119.  *  Local Functions
  120.  */
  121.  
  122. static void   server_start       (gint       port,
  123.                   gchar     *logfile);
  124. static gint   execute_command    (SFCommand *cmd);
  125. static gint   read_from_client   (gint       filedes);
  126. static gint   make_socket        (guint      port);
  127. static void   server_log         (gchar     *format,
  128.                      ...);
  129. static void   server_quit        (void);
  130.  
  131. static gint   server_interface   (void);
  132. static void   ok_callback        (GtkWidget *widget,
  133.                   gpointer   data);
  134.  
  135. /*
  136.  *  Global variables
  137.  */
  138. gint server_mode = FALSE;
  139.  
  140. /*
  141.  *  Local variables
  142.  */
  143. static gint   server_sock;
  144. static GList *command_queue = NULL;
  145. static gint   queue_length = 0;
  146. static gint   request_no = 0;
  147. static FILE  *server_log_file = NULL;
  148. static GHashTable *clientname_ht = NULL;
  149. static SELECT_MASK server_active, server_read;
  150.  
  151. static ServerInterface sint =
  152. {
  153.   NULL,  /*  port entry widget  */
  154.   NULL,  /*  log entry widget  */
  155.  
  156.   10008, /*  default port number  */
  157.   NULL,  /*  use stdout  */
  158.  
  159.   FALSE  /*  run  */
  160. };
  161.  
  162. extern gint   script_fu_done;
  163. extern char   siod_err_msg[];
  164. extern LISP   repl_return_val;
  165.  
  166. /*
  167.  *  Server interface functions
  168.  */
  169.  
  170. void
  171. script_fu_server_run (char     *name,
  172.               int       nparams,
  173.               GimpParam   *params,
  174.               int      *nreturn_vals,
  175.               GimpParam  **return_vals)
  176. {
  177.   static GimpParam values[1];
  178.   GimpPDBStatusType status = GIMP_PDB_SUCCESS;
  179.   GimpRunModeType run_mode;
  180.  
  181.   run_mode = params[0].data.d_int32;
  182.  
  183.   switch (run_mode)
  184.     {
  185.     case GIMP_RUN_INTERACTIVE:
  186.       if (server_interface ())
  187.     {
  188.       server_mode = TRUE;
  189.  
  190.       /*  Start the server  */
  191.       server_start (sint.port, sint.logfile);
  192.     }
  193.       break;
  194.  
  195.     case GIMP_RUN_NONINTERACTIVE:
  196.       /*  Set server_mode to TRUE  */
  197.       server_mode = TRUE;
  198.  
  199.       /*  Start the server  */
  200.       server_start (params[1].data.d_int32, params[2].data.d_string);
  201.       break;
  202.  
  203.     case GIMP_RUN_WITH_LAST_VALS:
  204.       status = GIMP_PDB_CALLING_ERROR;
  205.       g_warning ("Script-Fu server does not handle \"GIMP_RUN_WITH_LAST_VALS\"");
  206.       break;
  207.  
  208.     default:
  209.       break;
  210.     }
  211.  
  212.   *nreturn_vals = 1;
  213.   *return_vals = values;
  214.  
  215.   values[0].type = GIMP_PDB_STATUS;
  216.   values[0].data.d_status = status;
  217. }
  218.  
  219. void
  220. script_fu_server_listen (gint timeout)
  221. {
  222.   struct sockaddr_in clientname;
  223.   struct timeval tv;
  224.   struct timeval *tvp;
  225.   gint i;
  226.   gint size;
  227.  
  228.   /*  Set time struct  */
  229.   if (timeout)
  230.     {
  231.       tv.tv_sec = timeout / 1000;
  232.       tv.tv_usec = timeout % 1000;
  233.       tvp = &tv;
  234.     }
  235.   else
  236.     tvp = NULL;
  237.  
  238.   /* Block until input arrives on one or more active sockets or timeout occurs. */
  239.   server_read = server_active;
  240.   if (select (FD_SETSIZE, &server_read, NULL, NULL, tvp) < 0)
  241.     {
  242.       perror ("select");
  243.       return;
  244.     }
  245.  
  246.   /* Service all the sockets with input pending. */
  247.   for (i = 0; i < FD_SETSIZE; ++i)
  248.     if (FD_ISSET (i, &server_read))
  249.       {
  250.     if (i == server_sock)
  251.       {
  252.         /* Connection request on original socket. */
  253.         gint new;
  254.  
  255.         size = sizeof (clientname);
  256.         new = accept (server_sock,
  257.               (struct sockaddr *) &clientname,
  258.               &size);
  259.         if (new < 0)
  260.           {
  261.         perror ("accept");
  262.         return;
  263.           }
  264.  
  265.         /*  Associate the client address with the socket  */
  266.         g_hash_table_insert (clientname_ht,
  267.                  GINT_TO_POINTER (new),
  268.                  g_strdup (inet_ntoa (clientname.sin_addr)));
  269.         /*
  270.         server_log ("Server: connect from host %s, port %d.\n",
  271.             inet_ntoa (clientname.sin_addr),
  272.             (unsigned int) ntohs (clientname.sin_port));
  273.             */
  274.  
  275.         FD_SET (new, &server_active);
  276.       }
  277.     else
  278.       {
  279.         if (read_from_client (i) < 0)
  280.           {
  281.         /*  Disassociate the client address with the socket  */
  282.         g_hash_table_remove (clientname_ht, GINT_TO_POINTER (i));
  283.  
  284.         /*
  285.         server_log ("Server: disconnect from host %s, port %d.\n",
  286.                 inet_ntoa (clientname.sin_addr),
  287.                 (unsigned int) ntohs (clientname.sin_port));
  288.                 */
  289.  
  290.         close (i);
  291.         FD_CLR (i, &server_active);
  292.           }
  293.       }
  294.       }
  295. }
  296.  
  297. static void
  298. server_start (gint   port,
  299.           gchar *logfile)
  300. {
  301.   SFCommand *cmd;
  302.  
  303.   /*  Set up the clientname hash table  */
  304.   clientname_ht = g_hash_table_new (g_direct_hash, NULL);
  305.  
  306.   /*  Setup up the server log file  */
  307.   if (logfile)
  308.     server_log_file = fopen (logfile, "a");
  309.   else
  310.     server_log_file = NULL;
  311.   if (server_log_file == NULL)
  312.     server_log_file = stdout;
  313.  
  314.   /* Create the socket and set it up to accept connections. */
  315.   server_sock = make_socket (port);
  316.   if (listen (server_sock, 5) < 0)
  317.     {
  318.       perror ("listen");
  319.       return;
  320.     }
  321.  
  322.   server_log ("Script-fu initialized and listening...\n");
  323.  
  324.   /* Initialize the set of active sockets. */
  325.   FD_ZERO (&server_active);
  326.   FD_SET (server_sock, &server_active);
  327.  
  328.   /*  Loop until the server is finished  */
  329.   while (! script_fu_done)
  330.     {
  331.       script_fu_server_listen (0);
  332.  
  333.       while (command_queue)
  334.     {
  335.       /*  Get the current command  */
  336.       cmd = (SFCommand *) command_queue->data;
  337.  
  338.       /*  Process the command  */
  339.       execute_command (cmd);
  340.  
  341.       /*  Remove the command from the list  */
  342.       command_queue = g_list_remove (command_queue, cmd);
  343.       queue_length--;
  344.  
  345.       /*  Free the request  */
  346.       g_free (cmd->command);
  347.       g_free (cmd);
  348.     }
  349.     }
  350.  
  351.   server_quit ();
  352.  
  353.   /*  Close the server log file  */
  354.   if (server_log_file != stdout)
  355.     fclose (server_log_file);
  356. }
  357.  
  358. static gint
  359. execute_command (SFCommand *cmd)
  360. {
  361.   guchar buffer[RESPONSE_HEADER];
  362.   gchar *response;
  363.   time_t clock1, clock2;
  364.   gint response_len;
  365.   gint error;
  366.   gint i;
  367.  
  368.   /*  Get the client address from the address/socket table  */
  369.   server_log ("Processing request #%d\n", cmd->request_no);
  370.   time (&clock1);
  371.  
  372.   /*  run the command  */
  373.   if (repl_c_string (cmd->command, 0, 0, 1) != 0)
  374.     {
  375.       error = TRUE;
  376.       response_len = strlen (siod_err_msg);
  377.       response = siod_err_msg;
  378.  
  379.       server_log ("%s\n", siod_err_msg);
  380.     }
  381.   else
  382.     {
  383.       error = FALSE;
  384.  
  385.       if (TYPEP (repl_return_val, tc_string))
  386.     response = get_c_string (repl_return_val);
  387.       else
  388.     response = "Success";
  389.  
  390.       response_len = strlen (response);
  391.  
  392.       time (&clock2);
  393.       server_log ("Request #%d processed in %f seconds, finishing on %s",
  394.           cmd->request_no, difftime (clock2, clock1), ctime (&clock2));
  395.     }
  396.  
  397.   buffer[MAGIC_BYTE] = MAGIC;
  398.   buffer[ERROR] = (error) ? 1 : 0;
  399.   buffer[RSP_LEN_H_BYTE] = (guchar) (response_len >> 8);
  400.   buffer[RSP_LEN_L_BYTE] = (guchar) (response_len & 0xFF);
  401.  
  402.   /*  Write the response to the client  */
  403.   for (i = 0; i < RESPONSE_HEADER; i++)
  404.     if (write (cmd->filedes, buffer + i, 1) < 0)
  405.       {
  406.     /*  Write error  */
  407.     perror ("write");
  408.     return 0;
  409.       }
  410.  
  411.   for (i = 0; i < response_len; i++)
  412.     if (write (cmd->filedes, response + i, 1) < 0)
  413.       {
  414.     /*  Write error  */
  415.     perror ("write");
  416.     return 0;
  417.       }
  418.  
  419.   return 0;
  420. }
  421.  
  422. static gint
  423. read_from_client (gint filedes)
  424. {
  425.   SFCommand *cmd;
  426.   guchar buffer[COMMAND_HEADER];
  427.   gchar *command;
  428.   gchar *clientaddr;
  429.   time_t clock;
  430.   gint command_len;
  431.   gint nbytes;
  432.   gint i;
  433.  
  434.   for (i = 0; i < COMMAND_HEADER; i++)
  435.     {
  436.       if ((nbytes = read (filedes, buffer + i, 1)) < 0)
  437.     {
  438.       /* Read error. */
  439.       perror ("read");
  440.       return 0;
  441.     }
  442.       else if (nbytes == 0)
  443.     /* End-of-file. */
  444.     return -1;
  445.     }
  446.  
  447.   if (buffer[MAGIC_BYTE] != MAGIC)
  448.     {
  449.       server_log ("Error in script-fu command transmission.\n");
  450.       return -1;
  451.     }
  452.   command_len = (buffer [CMD_LEN_H_BYTE] << 8) | buffer [CMD_LEN_L_BYTE];
  453.   command = g_new (gchar, command_len + 1);
  454.  
  455.   for (i = 0; i < command_len;)
  456.     {
  457.       gint n = read (filedes, command + i, command_len - i);
  458.  
  459.       if (n <= 0)
  460.         {
  461.           if (n < 0 && errno == EINTR)
  462.             continue;
  463.  
  464.           server_log ("Error reading command.  Read %d out of %d bytes.\n",
  465.                       i, command_len);
  466.           g_free (command);
  467.           return -1;
  468.         }
  469.  
  470.       i += n;
  471.     }      
  472.  
  473.   command[command_len] = '\0';
  474.   cmd = g_new (SFCommand, 1);
  475.   cmd->filedes = filedes;
  476.   cmd->command = command;
  477.   cmd->request_no = request_no ++;
  478.  
  479.   /*  Add the command to the queue  */
  480.   command_queue = g_list_append (command_queue, cmd);
  481.   queue_length ++;
  482.  
  483.   /*  Get the client address from the address/socket table  */
  484.   clientaddr = g_hash_table_lookup (clientname_ht, GINT_TO_POINTER (cmd->filedes));
  485.   time (&clock);
  486.   server_log ("Received request #%d from IP address %s: %s on %s, [Request queue length: %d]",
  487.           cmd->request_no, clientaddr, cmd->command, ctime (&clock), queue_length);
  488.  
  489.   return 0;
  490. }
  491.  
  492. static gint
  493. make_socket (guint port)
  494. {
  495.   gint sock;
  496.   struct sockaddr_in name;
  497.   gint v = 1;
  498.  
  499.   /* Create the socket. */
  500.   sock = socket (PF_INET, SOCK_STREAM, 0);
  501.   if (sock < 0)
  502.     {
  503.       perror ("socket");
  504.       gimp_quit ();
  505.     }
  506.   setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &v, sizeof(v));
  507.  
  508.   /* Give the socket a name. */
  509.   name.sin_family = AF_INET;
  510.   name.sin_port = htons (port);
  511.   name.sin_addr.s_addr = htonl (INADDR_ANY);
  512.   if (bind (sock, (struct sockaddr *) &name, sizeof (name)) < 0)
  513.     {
  514.       perror ("bind");
  515.       gimp_quit ();
  516.     }
  517.  
  518.   return sock;
  519. }
  520.  
  521. static void
  522. server_log (gchar *format, ...)
  523. {
  524.   va_list args;
  525.   char *buf;
  526.  
  527.   va_start (args, format);
  528.   buf = g_strdup_vprintf (format, args);
  529.   va_end (args);
  530.  
  531.   fputs (buf, server_log_file);
  532.   if (server_log_file != stdout)
  533.     fflush (server_log_file);
  534. }
  535.  
  536. static void
  537. server_quit (void)
  538. {
  539.   int i;
  540.  
  541.   for (i = 0; i < FD_SETSIZE; ++i)
  542.     if (FD_ISSET (i, &server_active))
  543.       shutdown (i, 2);
  544. }
  545.  
  546. static gint
  547. server_interface (void)
  548. {
  549.   GtkWidget *dlg;
  550.   GtkWidget *table;
  551.  
  552.   INIT_I18N_UI();
  553.  
  554.   gimp_ui_init ("script-fu", FALSE);
  555.  
  556.   dlg = gimp_dialog_new (_("Script-Fu Server Options"), "script-fu",
  557.              gimp_standard_help_func, "filters/script-fu.html", 
  558.              GTK_WIN_POS_MOUSE,
  559.              FALSE, TRUE, FALSE,
  560.  
  561.              _("OK"), ok_callback,
  562.              NULL, NULL, NULL, TRUE, FALSE,
  563.              _("Cancel"), gtk_widget_destroy,
  564.              NULL, 1, NULL, FALSE, TRUE,
  565.  
  566.              NULL);
  567.  
  568.   gtk_signal_connect (GTK_OBJECT (dlg), "destroy",
  569.               GTK_SIGNAL_FUNC (gtk_main_quit),
  570.               NULL);
  571.  
  572.   /*  The table to hold port & logfile entries  */
  573.   table = gtk_table_new (2, 2, FALSE);
  574.   gtk_table_set_col_spacings (GTK_TABLE (table), 4);
  575.   gtk_table_set_row_spacings (GTK_TABLE (table), 2);
  576.   gtk_container_set_border_width (GTK_CONTAINER (table), 4);
  577.   gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dlg)->vbox), table, TRUE, TRUE, 0);
  578.  
  579.   /*  The server port  */
  580.   sint.port_entry = gtk_entry_new ();
  581.   gtk_entry_set_text (GTK_ENTRY (sint.port_entry), "10008");
  582.   gimp_table_attach_aligned (GTK_TABLE (table), 0, 0, 
  583.                  _("Server Port:"), 1.0, 0.5,
  584.                  sint.port_entry, 1, TRUE);
  585.  
  586.   /*  The server logfile  */
  587.   sint.log_entry = gtk_entry_new ();
  588.   gimp_table_attach_aligned (GTK_TABLE (table), 0, 1, 
  589.                  _("Server Logfile:"), 1.0, 0.5,
  590.                  sint.log_entry, 1, TRUE);
  591.  
  592.   gtk_widget_show (table);
  593.   gtk_widget_show (dlg);
  594.  
  595.   gtk_main ();
  596.   gdk_flush ();
  597.  
  598.   return sint.run;
  599. }
  600.  
  601. static void
  602. ok_callback (GtkWidget *widget,
  603.          gpointer   data)
  604. {
  605.   sint.port = atoi (gtk_entry_get_text (GTK_ENTRY (sint.port_entry)));
  606.   sint.logfile = g_strdup (gtk_entry_get_text (GTK_ENTRY (sint.log_entry)));
  607.   sint.run = TRUE;
  608.  
  609.   gtk_widget_destroy (GTK_WIDGET (data));
  610. }
  611.  
  612. #endif /* G_OS_WIN32 */
  613.